#pragma once

#include <vector>
#include <limits.h>

#include "Types.h"
#include "Chunk2.h"



namespace MemMgr
{
	class FixedSizeAllocator2
	{
	public:

		typedef std::vector<Chunk2> ChunksVec;
		typedef ChunksVec::iterator ChunksIter;
		typedef ChunksVec::const_iterator ChunksCIter;
		typedef T_U32 BitMap;

		static const size_t BITMAP_NUM_BIT = sizeof(BitMap)*CHAR_BIT;
		static const size_t LOG2_BITMAP_NUM_BIT = 0x00000005;
		static const size_t BITMAP_CACHE_SIZE = BITMAP_NUM_BIT * BITMAP_NUM_BIT;

		static const size_t MAX_CHUNKS_NUM = static_cast<size_t>(~0);

		FixedSizeAllocator2(void);
		FixedSizeAllocator2(const FixedSizeAllocator2& iOther);
		~FixedSizeAllocator2(void);
		FixedSizeAllocator2& operator=(const FixedSizeAllocator2& iOther);

		inline size_t GetBlockSize(void) const;

		void Swap(FixedSizeAllocator2& ioOther);

		void* Allocate(void);
		void Deallocate(void* iPtr);
		void Initialize(const size_t iBlockSize, const size_t iPageSize);

	private:

		inline void SetBitAvailable(const size_t iChunkIndex);
		inline void SetBitUnavailable(const size_t iChunkIndex);
		void CreateNewChunk(void);
		size_t SearchChunk(void* iPtr) const;

		ChunksVec mChunks;

		BitMap mSecondLevelAvailableChunksBitMaps[BITMAP_NUM_BIT];
		BitMap mFirstLevelAvailableChunksBitMap;

		size_t mLastAllocationChunk;
		size_t mLastDeallocationChunk;
		size_t mOnlyOneEmptyChunk; // Index to the only empty chunk if there is one... else nullptr.

		size_t mBlockSize;
		Chunk2::BlockCounter mNumBlocksPerChunk;
	};



	inline size_t FixedSizeAllocator2::GetBlockSize(void) const
	{
		return mBlockSize;
	}


	inline void FixedSizeAllocator2::SetBitAvailable(const size_t iChunkIndex)
	{
		const size_t secondLevelIndex = iChunkIndex >> LOG2_BITMAP_NUM_BIT;
		const size_t secondLevelBitPos = iChunkIndex & (BITMAP_NUM_BIT - 1);
		mFirstLevelAvailableChunksBitMap |= (0x00000001 << secondLevelIndex);
		mSecondLevelAvailableChunksBitMaps[secondLevelIndex] |= 0x00000001 << secondLevelBitPos;
	}

	inline void FixedSizeAllocator2::SetBitUnavailable(const size_t iChunkIndex)
	{
		const size_t secondLevelIndex = iChunkIndex >> LOG2_BITMAP_NUM_BIT;
		const size_t secondLevelBitPos = iChunkIndex & (BITMAP_NUM_BIT - 1);
		mSecondLevelAvailableChunksBitMaps[secondLevelIndex] &= ~(0x00000001 << secondLevelBitPos);
		if(0 == mSecondLevelAvailableChunksBitMaps[secondLevelIndex])
			mFirstLevelAvailableChunksBitMap &= ~(0x00000001 << secondLevelIndex);
		
	}
};